﻿using System;
using System.Xml.Linq;
using System.ComponentModel;

namespace Duellum.Core
{
	//inpired in System.Xml.Linq.XName
	public sealed class DuelTypeId : IEquatable<DuelTypeId>, IComparable<DuelTypeId>, IXSerializable//, ISerializable
	{
		private readonly string stringValue;
		private readonly int hashCode;
		
		public DuelTypeId(string id)
		{
			stringValue = id;
			hashCode = (stringValue == null) ? 0 : stringValue.GetHashCode();
		}
		
		static public DuelTypeId Get(string id)
		{
			return new DuelTypeId(id);
		}

		public override string ToString()
		{
			return stringValue;
		}

		public override int GetHashCode()
		{
			return hashCode;
		}

		static public implicit operator DuelTypeId(string id)
		{
			return new DuelTypeId(id);
		}

		static public explicit operator string(DuelTypeId id)
		{
			return id.stringValue;
		}
		
		//static public bool operator ==(DuelTypeId typeId, string typeStr)
		//{
		//    if (typeStr == null) return ((object)typeId == null);
		//    return typeId.Equals((DuelTypeId)typeStr);
		//}
		//static public bool operator !=(DuelTypeId typeId, string typeStr)
		//{
		//    if (typeStr == null) return ((object)typeId != null);
		//    return !typeId.Equals((DuelTypeId)typeStr);
		//}

		static public bool operator ==(DuelTypeId one, DuelTypeId other)
		{
			if ((object)one == null) return ((object)other == null);
		    return one.Equals(other);
		}
		static public bool operator !=(DuelTypeId one, DuelTypeId other)
		{
			if ((object)one == null) return ((object)other != null);
		    return !one.Equals(other);
		}

		public override bool Equals(object obj)
		{
			if (obj is DuelTypeId)	return this.Equals((DuelTypeId)obj);
			if (obj is string)		return this.Equals((DuelTypeId)obj);
			return false;
		}

		public bool Equals(DuelTypeId other)
		{
			if (other == null) return false;
			return this.stringValue == other.stringValue;
		}
		
		public XObject ToXml(XName name)
		{
			return new XAttribute(name, this);
		}

		public int CompareTo(DuelTypeId other)
		{
			if (other == null) return 1;
			
			return this.stringValue.CompareTo(other.stringValue);
		}
	}
	
	//public interface IDuelAbstraction
	//{
	//    bool IsDescendantOf(DuelType type);
	//    bool IsDescendantOf(DuelTypeId id);
	//    DuelTypeId Id { get; }
	//}

	public abstract class DuelAbstraction : AugObject//, IDuelAbstraction
	{
		public abstract bool IsDescendantOf(DuelTypeId id);
		public abstract bool IsDescendantOf(DuelType type);
		
		public abstract DuelTypeId Id { get; set; }

	    public bool Is(DuelTypeId id) { return this.IsDescendantOf(id); }
	    public bool Is(DuelType type) { return this.IsDescendantOf(type); }
	}
	
	//[Serializable]
	public abstract class DuelType : DuelAbstraction, ISupportInitialize
	{
		static public readonly AugProperty IsAbstractProp =
			AugProperty.Create(
				"IsAbstract", typeof(bool), typeof(DuelType), AugPropertyFlags.DoesNotInherit, null
			);

		public override AugObject AugParent { get { return this.BaseType; } }
		public DuelType BaseType { get; set; }
		public override DuelTypeId Id { get; set; }
		
		public DuelDomain Domain { get; internal set; }

		protected DuelType(DuelTypeId id)
		{
			this.Id = id;
		}

		//protected DuelType(DuelTypeId id, DuelTypeId baseType)
		//{
		//    this.Id = id;
		//    if (baseType == null) baseType = DuelRootType.TypeId;
		//    if (baseType == Id) baseType = null;
		//    SetBaseTypeId(baseType);
		//}

		public void BeginInit()
		{}

		public void EndInit()
		{
			if (BaseType == null) SetBaseTypeId(GetDefaultBaseType());
			
			ExchangeReferencesToConcreteObjs(this.Domain);
		}
		
		protected virtual DuelTypeId GetDefaultBaseType()
		{
			return DuelRootType.TypeId;
		}

		internal void SetBaseTypeId(DuelTypeId id)
		{
			if (BaseType != null && BaseType.Id == id) return; //BaseType is already set
			
			BaseType = (id == null) ? null : new DuelTypeReference(id);
		}
		
		private void ExchangeReferencesToConcreteObjs(DuelDomain domain)
		{
			if (BaseType is DuelTypeReference) {
				//*
				BaseType = domain.GetType(BaseType.Id);
				/*/
				DuelType concreteParent;
				if (domain.TryGetType(Parent.Id, out concreteParent)) {
					this.Parent = concreteParent;
					return true;
				}
				return false;
				//*/
			}
		}

		public bool IsAbstract
		{
			get { return (bool)GetValue(IsAbstractProp); }
			set { SetValue(IsAbstractProp, value); }
		}
		public bool IsConcrete { get { return !IsAbstract; } }
		
		public virtual BaseDuelObj CreateObj()
		{
			throw new NotSupportedException();
		}

		public sealed override bool IsDescendantOf(DuelType type)
		{
			if (type == this) return true;
			if (BaseType == null) return false;
			return BaseType.IsDescendantOf(type);
		}

		public sealed override bool IsDescendantOf(DuelTypeId id)
		{
			if (id == this.Id) return true;
			if (BaseType == null) return false;
			return BaseType.IsDescendantOf(id);
		}
		
		internal void ApplyDecoration(DuelDecoration decoration)
		{
			foreach (var v in decoration.GetLocalValues()) this.SetValue(v.Key, v.Value);
		}
		
		public override XElement ToXml(int maxSubLevels)
		{
			return base.ToXml(maxSubLevels, this.Id.ToXml("id"));
		}

		public override string ToString()
		{
			return this.ToXml(0).ToString(SaveOptions.DisableFormatting);
		}
	}

	internal sealed class DuelTypeReference : DuelType
	{
		public DuelTypeReference(DuelTypeId id) : base(id)
		{
			IsAbstract = true;
		}

		protected override DuelTypeId GetDefaultBaseType() { return null; }
	}
	
	[DuelType("Decoration")]
	public sealed class DuelDecoration : DuelType
	{
		public DuelDecoration(DuelTypeId id) : base(id) {}

		protected override DuelTypeId GetDefaultBaseType() { return null; }
	}

	public sealed class DuelRootType : DuelType
	{
		static public readonly DuelTypeId TypeId = "root";
		
		public DuelRootType() : base(TypeId)
		{
			IsAbstract = true;
		}

		protected override DuelTypeId GetDefaultBaseType() { return null; }
	}

	internal sealed class DynamicDuelType : DuelType
	{
		public DynamicDuelType(DuelTypeId id, DuelType parent) : base(id)
		{
			this.BaseType = parent;
		}
	}

	public abstract class BaseDuelObj : DuelAbstraction
	{
		public override DuelTypeId Id {
			get { return Type.Id; }
			set { throw new NotSupportedException(); }
		}
		
		public DuelType Type { get; private set; }

		public override AugObject AugParent { get { return this.Type; } }

		public BaseDuelObj(DuelType type)
		{
			Type = type;
		}

		public override XElement ToXml(int maxSubLevels)
		{
			return this.ToXml(maxSubLevels, Type.Id.ToXml("id"));
		}

		public override string ToString()
		{
			return this.ToXml(0).ToString(SaveOptions.DisableFormatting);
		}
		
		public sealed override bool IsDescendantOf(DuelType type)
		{
			if (Type == null) return false;
			return Type.IsDescendantOf(type);
		}

		public sealed override bool IsDescendantOf(DuelTypeId id)
		{
			if (Type == null) return false;
			return Type.IsDescendantOf(id);
		}
	}
}
